package com.wang.lottery.domain.support.ids.policy;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.net.NetUtil;
import cn.hutool.core.util.IdUtil;
import com.wang.lottery.domain.support.ids.IIdGenerator;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

/**
 * @author wjh
 * @create 2023-11-05 17:36
 * hutool 工具包下的雪花算法，15位雪花算法
 */
@Component("snowFlake")
public class SnowFlake implements IIdGenerator {

    private Snowflake snowflake;

    /**
     * 这个init方法还没有生成id，只是根据这台电脑ip生成它的唯一标识，比如这台电脑在第几机房，第几号机器，
     * 然后调用IdUtil.createSnowflake方法，利用这些信息生成一个Snowflake对象，此时Snowflake有了这台电脑的唯一id（机房id+机器id），
     * 但是还没有时间戳，还需要调用snowflake.nextId()方法才能根据时间戳、机房id、机器id和一个毫秒内产生的不同 id去生成全局唯一id
     */
    @PostConstruct
    public void init() {
        // 0 ~ 31 位，可以采用配置的方式使用
        long workerId;
        try {
            workerId = NetUtil.ipv4ToLong(NetUtil.getLocalhostStr());
        } catch (Exception e) {
            workerId = NetUtil.getLocalhostStr().hashCode();
        }

        /**
         * workerId是机器，dataCenterId是机房id，范围都是0到31，也就是最多有32个机房，每个机房下最多有32个机器，一个1024个机器
         * 比如我这台电脑，调用NetUtil.ipv4ToLong(NetUtil.getLocalhostStr());生成数值是：3232273153，转化为二进制也就是
         * 1100 0000 1010 1000 1001 0011 0000 0001
         *                                    >>16  一共32位，右移16位，也就是中间砍一半，取前16位
         * ———————————————————————————————————————
         * 0000 0000 0000 0000 1100 0000 1010 1000 = 49,320     结果等于49,320
         * &                                        和31与运算，也就是和0001 1111与运算
         * 0000 0000 0000 0000 0000 0000 0001 1111
         * ———————————————————————————————————————
         * 0000 0000 0000 0000 0000 0000 0000 1000 = 8  最后结果等于8，也就是这个台机器id为8，二进制为1000
         * 思考：一个机房下最多32个机器，那么为什么要&31，而不是&32？
         * 因为和任意一个数按位与，结果都会小于或者等于这个数，所以workerId<=31，因为在雪花算法中，机器id和机房id都是占5位，如果所以workerId等于32，
         * 那二进制就是10 0000，占6位了，而且点击构造方法也发现代码里有两层判断
         * if (workerId <= 31L && workerId >= 0L) {
         *      if (dataCenterId <= 31L && dataCenterId >= 0L) {
         * 所以这里一定要 & 31，不过>>16是个不必要的操作，可以用也可以不用，因为拿3232273153&31结果也<=31
         * 有关雪花算法的文章：https://www.cnblogs.com/hzzjj/p/15117771.html
         *
         */
        workerId = workerId >> 16 & 31;

        /** 机房id，由于这里是一台机器，所以这里就把机房id写死为1，如果有多个机房那么写真实的机房序号了，也是0到31的范围 */
        long dataCenterId = 1L;
        snowflake = IdUtil.getSnowflake(workerId, dataCenterId);
    }
    
    @Override
    public long nextId() {
        return snowflake.nextId();
    }
}
